Skip to content

feat(parse): accept explicit + sign in e-notation exponent - #248

Merged
thedavidmeister merged 5 commits into
mainfrom
feat/issue-214-positive-exponent-sign
Oct 5, 2026
Merged

thedavidmeister merged 5 commits into
mainfrom
feat/issue-214-positive-exponent-sign

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

Refs rainlanguage/rainlang#214.

What

LibParseDecimalFloat.parseDecimalFloatInline accepts an explicit + after e/E, so 1e+2, 1.0e+2 and 1E+2 parse the same as 1e2.

An exponent takes one sign: + is accepted only where - would be, never after it. 1e-+2 and 1e+-2 reject with MalformedExponentDigits, as does 1e+ with no digits (same as 1e-).

Tests

  • testParseLiteralDecimalFloatPositiveExponentSign: nine accepted forms, including signed coefficients and decimals.
  • testParseLiteralDecimalFloatPositiveENoDigits: 1e+ and 0.0e+ reject.
  • testParseLiteralDecimalFloatExponentSignAfterNegativeSign / ...NegativeSignAfterPositiveSign: 1e-+2 and 1e+-2 reject. An earlier revision of this PR parsed 1e-+2 as 1e2.

Scope after the library/deploy split

main was merged in. The deploy constants and ABI artifact this PR used to touch now live in rain.math.float.deploy; those changes were dropped. No redeploy is involved. The diff is the parser and its tests only.

🤖 Generated with Claude Code

`1e+2`, `1.0e+2`, `1E+2` etc. now parse identically to `1e2`.
`1e+` with no digits still rejects with MalformedExponentDigits.

Fixes rainlanguage/rainlang#214.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR extends LibParseDecimalFloat to accept an optional explicit + sign immediately after the exponent marker (e/E) in decimal float literals, mirroring existing support for -. Tests are added covering acceptance of e+ variants and the MalformedExponentDigits failure when no digits follow.

Changes

Positive Exponent Sign Support

Layer / File(s) Summary
Exponent sign parsing
src/lib/parse/LibParseDecimalFloat.sol
Imports CMASK_PLUS_SIGN and updates exponent parsing to detect and skip an optional + after e/E, adjusting eStart so digit parsing starts correctly.
Exponent sign tests
test/src/lib/parse/LibParseDecimalFloat.t.sol
Adds tests confirming e+ parses equivalently to e across several inputs, and that + without following digits reverts with MalformedExponentDigits.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Related PRs: None identified.

Suggested labels: enhancement, tests

Suggested reviewers: None identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: accepting an explicit + sign in decimal-float exponents.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-214-positive-exponent-sign

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/parse/LibParseDecimalFloat.sol (1)

141-151: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Mixed -/+ exponent signs silently mis-parsed instead of rejected.

The new + check runs unconditionally after the - skip, without verifying that no negative sign was already consumed. For input like "1e-+2":

  • Line 145 skips the -, advancing cursor to point at +.
  • Line 148 then matches + at that position, advances cursor past it, and overwrites eStart to point at 2 — discarding the previously consumed - entirely.
  • The exponent parses as +2 instead of being rejected, silently dropping the sign that was present in the input.

Previously (before this PR) "1e-+2" failed with MalformedExponentDigits because + is not a digit. Now it succeeds with an incorrect (wrong-sign) exponent value. This is a real regression for a decimal-float parser that presumably feeds downstream numeric/financial logic.

🐛 Proposed fix: only accept `+` when no `-` was already consumed
                 uint256 eStart = cursor;
-                cursor = LibParseChar.skipMask(cursor, end, CMASK_NEGATIVE_SIGN);
-                // Skip an optional explicit positive sign (e.g. 1e+2). Advance
-                // eStart past it so the int parser only sees the digit string.
-                if (LibParseChar.isMask(cursor, end, CMASK_PLUS_SIGN) == 1) {
-                    cursor++;
-                    eStart = cursor;
-                }
+                uint256 cursorBeforeSign = cursor;
+                cursor = LibParseChar.skipMask(cursor, end, CMASK_NEGATIVE_SIGN);
+                // Skip an optional explicit positive sign (e.g. 1e+2), but only
+                // if a negative sign was not already consumed above. Advance
+                // eStart past it so the int parser only sees the digit string.
+                if (cursor == cursorBeforeSign && LibParseChar.isMask(cursor, end, CMASK_PLUS_SIGN) == 1) {
+                    cursor++;
+                    eStart = cursor;
+                }

Please add test cases for "1e-+2" and "0.0e-+1" asserting MalformedExponentDigits (or another rejection), to lock in the correct behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/parse/LibParseDecimalFloat.sol` around lines 141 - 151, The
exponent-sign handling in LibParseDecimalFloat.parseDecimalFloat currently
accepts a plus sign even after a minus has already been consumed, which lets
mixed signs like 1e-+2 slip through. Update the exponent parsing logic around
the eValue/cursor/eStart block so the optional CMASK_PLUS_SIGN is only
recognized when no CMASK_NEGATIVE_SIGN was skipped, and otherwise leave the
parser to fail with MalformedExponentDigits. Add regression tests covering 1e-+2
and 0.0e-+1 to ensure mixed signs are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/lib/parse/LibParseDecimalFloat.sol`:
- Around line 141-151: The exponent-sign handling in
LibParseDecimalFloat.parseDecimalFloat currently accepts a plus sign even after
a minus has already been consumed, which lets mixed signs like 1e-+2 slip
through. Update the exponent parsing logic around the eValue/cursor/eStart block
so the optional CMASK_PLUS_SIGN is only recognized when no CMASK_NEGATIVE_SIGN
was skipped, and otherwise leave the parser to fail with
MalformedExponentDigits. Add regression tests covering 1e-+2 and 0.0e-+1 to
ensure mixed signs are rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 12c3e737-fa22-481d-a6a1-9aceddbfddd3

📥 Commits

Reviewing files that changed from the base of the PR and between 774ed07 and 6429749.

📒 Files selected for processing (2)
  • src/lib/parse/LibParseDecimalFloat.sol
  • test/src/lib/parse/LibParseDecimalFloat.t.sol

thedavidmeister and others added 2 commits July 3, 2026 17:05
Regenerate DecimalFloat.json artifact after adding explicit + sign
support in e-notation exponent parsing.

Co-Authored-By: Claude <noreply@anthropic.com>
…[3b-attempt]

ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS and DECIMAL_FLOAT_CONTRACT_HASH are
now set to the values computed from this PR's bytecode (explicit + sign
in e-notation exponent changes source). Computed by testDeployAddress
and testExpectedCodeHashDecimalFloat failure output:
address: 0x9b0eF7FbDF04b3A58989860Dea0534C978d64464
hash: 0x369ac78d686dfdf1d41771dc7bf321dcd339b0a454dc9cf6c37eff579c3a0c3a

Co-Authored-By: Claude <noreply@anthropic.com>
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Producer note: WAITING-DEPLOY. Deploy constants now regenerated to this PR's bytecode (testDeployAddress/testExpectedCodeHash pass); the remaining red is the 5 testProdDeployment* network pins ('DecimalFloat not deployed') — same condition as main, needs the routine interactive pre-merge Zoltu deploy from this branch, not a code fix.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Producer note: state change on the reds. Beyond the 5 WAITING-DEPLOY prod pins (unchanged), rainix-sol/static and rs-static now fail on the no-skips gate: test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol:24 vm.skip(true). That line is IDENTICAL on current main (774ed07) — a pre-existing main red from the newly tightened rainix no-skip rule, not this PR's diff, so no fix belongs here. Org-level collision, also hitting raindex (#2779, same pattern on raindex main): the no-skip gate vs the established SKIP-on-registry-unreachable FFI pattern needs a human ruling (allowlist the pattern in the gate, or restructure the tagged-constants tests org-wide).

@thedavidmeister thedavidmeister added the ai:needs-work Needs rework — the producer's inbox (vetter verdict or human ruling) label Jul 6, 2026
@thedavidmeister

thedavidmeister commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 ai:vetter
Reviewed bef4e86: reject — missing/false QA evidence: no QA-GUIDE section-8 block in body or comments; plus-sign exponent parse
cost 420 — exponent-sign parse change plus artifact regen

@thedavidmeister thedavidmeister added ai:blocked-on AI producer: blocked on a dependency PR and removed ai:needs-work Needs rework — the producer's inbox (vetter verdict or human ruling) labels Jul 16, 2026
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Blocked-on: blocked on #253 landing: static reds are main's vm.skip file (deleted by #253); after #253 merges, this branch needs a base merge + pin regen + its own pre-merge deploy of the parse-change bytecode

baku-ccron and others added 2 commits October 5, 2026 16:10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve-exponent-sign

# Conflicts:
#	crates/float/abi/DecimalFloat.json
#	src/lib/deploy/LibDecimalFloatDeploy.sol
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thedavidmeister
thedavidmeister merged commit d2bbc43 into main Oct 5, 2026
7 of 9 checks passed
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@linear

linear Bot commented Oct 5, 2026

Copy link
Copy Markdown

RAI-2909

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai:blocked-on AI producer: blocked on a dependency PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant